#include "atom.h"
#include <pthread.h>    
#include "syslog.h"
#include "debug.h"


static int _enable_int_cpu = 1;
pthread_mutex_t _interrupt_mutex = PTHREAD_MUTEX_INITIALIZER;  // 初始化互斥锁

int get_cc(void)
{
	if (_enable_int_cpu == 1) {
        pthread_mutex_lock(&_interrupt_mutex);
		_enable_int_cpu = 0;
		return 0;
	}else {
		return 1;
	}
}

void set_cc(int ccr)
{
	if (ccr == 0) {
        pthread_mutex_unlock(&_interrupt_mutex);
		_enable_int_cpu = 1;
	}
}


/** Set to TRUE when OS is started and running threads */
uint8_t atomOSStarted = FALSE;

/* Number of nested interrupts */
static int atomIntCnt = 0;
static pthread_attr_t thread_attr;

extern uint8_t atomOSInit(void* idle_thread_stack_bottom, uint32_t idle_thread_stack_size, uint8_t idle_thread_stack_check)
{
    //@note 在vos的情况下，因为非实时调度器不能很好的模拟idle.保证优先级最低。
    
    // 初始化线程属性
    pthread_attr_init(&thread_attr);

    // 设置线程调度策略为实时的 FIFO 调度
    pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);

	return ATOM_OK;
}

void atomOSStart(void)
{
	atomOSStarted = TRUE;
}

/**
 * \b atomIntEnter
 *
 * Interrupt handler entry routine.
 *
 * Must be called at the start of any interrupt handlers that may
 * call an OS primitive and make a thread ready.
 *
 * @return None
 */
void atomIntEnter(void)
{
	/* Increment the interrupt count */
	atomIntCnt++;
}

/**
 * \b atomSched
 *
 * This is an internal function not for use by application code.
 *
 * This is the main scheduler routine. It is called by the various OS
 * library routines to check if any threads should be scheduled in now.
 * If so, the context will be switched from the current thread to the
 * new one.
 *
 * The scheduler is priority-based with round-robin performed on threads
 * with the same priority. Round-robin is only performed on timer ticks
 * however. During reschedules caused by an OS operation (e.g. after
 * giving or taking a semaphore) we only allow the scheduling in of
 * threads with higher priority than current priority. On timer ticks we
 * also allow the scheduling of same-priority threads - in that case we
 * schedule in the head of the ready list for that priority and put the
 * current thread at the tail.
 *
 * @param[in] timer_tick Should be TRUE when called from the system tick
 *
 * @return None
 */
void atomSched(uint8_t timer_tick)
{
    //do nothing.
}

/**
 * \b atomIntExit
 *
 * Interrupt handler exit routine.
 *
 * Must be called at the end of any interrupt handlers that may
 * call an OS primitive and make a thread ready.
 *
 * This is responsible for calling the scheduler at the end of
 * interrupt handlers to determine whether a new thread has now
 * been made ready and should be scheduled in.
 *
 * @param timer_tick TRUE if this is a timer tick
 *
 * @return None
 */
void atomIntExit(uint8_t timer_tick)
{
    /* Decrement the interrupt count */
    atomIntCnt--;

    /* Call the scheduler */
    atomSched(timer_tick);
}


typedef void* (*START_ROUTINE) (void*);

static SLIST_HEAD(KernelList, KernelNode) _kernelTcbList;

#define KERNEL_TCB_INSERT(tcb) \
    SLIST_INSERT_HEAD(&_kernelTcbList, (KernelNode*)&tcb->node, node);

#define KERNEL_TCB_NODE_TCB(item) \
    ((ATOM_TCB*)((uint8_t*)(item) - offsetof(ATOM_TCB, node)))

enum ThreadStatus {
    TS_READY = 0,
    TS_RUN,
    TS_SUSP,
    TS_TERM
};

/** This is a pointer to the TCB for the currently-running thread */
ATOM_TCB* atomCurrentContext(void)
{
    KernelNode* item;
    CRITICAL_STORE;

    CRITICAL_START();
    pthread_t tid = pthread_self();

    SLIST_FOREACH(item, &_kernelTcbList, node) {
        ATOM_TCB* pTcb = KERNEL_TCB_NODE_TCB(item);
        if (pTcb->save_ptr == (uint32_t)tid) {
            return pTcb;
        }
    }

    CRITICAL_END();

    return NULL;
}

#ifdef ATOM_USE_KERNEL_DBG
void atomThreadStatusPrint(void)
{
    KernelNode* item;
    CRITICAL_STORE;
   
    uint8_t priority;
    const char* name;

    /* KPrintf is the stack padding */
    KPrint("thread   pri\r\n");
    KPrint("----------------------------------------------------\r\n");

    SLIST_FOREACH(item, &_kernelTcbList, node) {
        CRITICAL_START();
        ATOM_TCB* pTcb = KERNEL_TCB_NODE_TCB(item);
        name = pTcb->name;
        priority = pTcb->priority;
        KPrintf("%-8s %3d\r\n", name, (uint32_t)priority);
        CRITICAL_END();
    }
}
#endif

//
uint8_t atomThreadCreate(ATOM_TCB* tcb_ptr, uint8_t priority, void (*entry_point)(void*), void* entry_param, void* stack_bottom, uint32_t stack_size, uint8_t stack_check)
{
    uint8_t ret = ATOM_OK;

    if ((tcb_ptr == NULL) || (entry_point == NULL) || (stack_bottom == NULL)
        || (stack_size == 0))
    {
        /* Bad parameters */
        ret = ATOM_ERR_PARAM;
    }
    else {
        int cc = get_cc();
        KERNEL_TCB_INSERT(tcb_ptr);
        set_cc(cc);

        struct sched_param param;

        pthread_t thread;
        param.sched_priority = priority;
        pthread_attr_setschedparam(&thread_attr, &param);

        int ret = pthread_create(&thread, &thread_attr, (START_ROUTINE)entry_point, (void*)entry_param);

        if (ret == 0) {
            tcb_ptr->priority = priority;
            tcb_ptr->entry_point = entry_point;
            tcb_ptr->entry_param = entry_param;
            // 保存threadid到tcb中
            tcb_ptr->save_ptr = (uint32_t)thread;
        }
        else {
            ret = ATOM_ERR_PARAM;
        }
    }

    return ret;
}

#ifdef ATOM_STACK_CHECKING
uint8_t atomThreadStackCheck(ATOM_TCB* tcb_ptr, uint32_t* used_bytes, uint32_t* free_bytes)
{
    SYSLOG(LOG_NOTICE, "atomThreadStackCheck not implemented under vos!");
    return 0;
}

uint8_t atomIdleSetHook(void (*hook)(void))
{
    SYSLOG(LOG_NOTICE, "atomIdleSetHook not implemented under vos!");
    return 0;
}
#endif

void archContextSwitch(ATOM_TCB* old_tcb_ptr, ATOM_TCB* new_tcb_ptr)
{
    SYSLOG(LOG_NOTICE, "archContextSwitch not implemented under vos!");
}


void archFirstThreadRestore(ATOM_TCB* new_tcb_ptr)
{
    SYSLOG(LOG_NOTICE, "archFirstThreadRestore not implemented under vos!");
}

/**
 * Initialise a threads stack so it can be scheduled in by
 * archFirstThreadRestore or the pend_sv_handler.
 */
void archThreadContextInit(ATOM_TCB* tcb_ptr, void* stack_top,
    void (*entry_point)(void*), void* entry_param)
{
    SYSLOG(LOG_NOTICE, "archThreadContextInit not implemented under vos!");
}

